#include "undo.h"

struct edit_gui_io_undoAction *_undo = NULL;
struct edit_gui_io_undoAction *_redo = NULL;

void edit_gui_io_addUndoAction(int action, unsigned int data, int loc)
{
	struct edit_gui_io_undoAction *nue = malloc(sizeof *nue);

	if (_undo == NULL)
		nue->next = NULL;
	else
		nue->next = _undo;

	_undo = nue;
	nue->action = action;
	nue->data = data;
	nue->loc = loc;

	edit_gui_io_clearRedo();
}

void edit_gui_io_addUndoActionPtr(int action, void *data, int loc)
{
	struct edit_gui_io_undoAction *nue = malloc(sizeof *nue);

	if (_undo == NULL)
		nue->next = NULL;
	else
		nue->next = _undo;

	_undo = nue;
	nue->action = action;
	nue->data2 = data;
	nue->loc = loc;
	edit_gui_io_clearRedo();
}

void edit_gui_io_clearUndo()
{
	while (_undo != NULL) {
		if (_undo->action == EDIT_GUI_IO_ACTION_CUT ||
		    _undo->action == EDIT_GUI_IO_ACTION_PASTE)
			free(_undo->data2);
		struct edit_gui_io_undoAction *p = _undo->next;
		free(_undo);
		_undo = p;
	}
}
void edit_gui_io_undo()
{
	if (_undo == NULL)
		return;
	struct edit_gui_io_undoAction *item = _undo;
	_undo = _undo->next;

	switch (item->action) {
	case 0:
		{

			break;
		}
	case 1:
		{
			//handling space:
			if (item->data == ' ' || item->data == '	'
			    || item->data == '\n') {
				edit_gui_al_removeChar(item->loc);
				break;
			}


			struct edit_gui_io_undoAction *p = item->next;
			int loc = item->loc;
			int lastLoc = item->loc;

			while (p != NULL && p->loc >= 0 && p->action == 1
			       && (p->data != ' ' && p->data != '	'
				   && p->data != '\n')
			       && loc - p->loc == 1) {
				loc = p->loc;
				p = p->next;
			}

			
			while (item != p) {
				_undo = item->next;
				edit_gui_io_addRedo(item);
				item = _undo;
			}	//passing onto undo
			item = NULL;
			edit_gui_io_updateTags(loc, -1 * (lastLoc - loc + 1));
			edit_gui_al_removeStr(loc, lastLoc - loc + 1);
			break;
		}
	case 2:
		{
			edit_gui_io_updateTags(item->loc, 1);
			edit_gui_al_insertChar(item->loc, item->data);
			break;
		}
	case 4:
		{
			edit_gui_al_insertStr(item->loc, item->data2);
			edit_gui_io_updateTags(item->loc,
					       elog_gui_al_strlen(item->
								  data2));
			break;
		}
	case 5:
		{
			edit_gui_io_updateTags(item->loc,
					       -1 *
					       elog_gui_al_strlen(item->
								  data2));
			edit_gui_al_removeStr(item->loc,
					      elog_gui_al_strlen(item->
								 data2));
			break;
		}
	case 6:
		{
			struct elog_xml_link *lnk = item->data2;
			edit_gui_io_removeLink(lnk->start - 1);
			break;
		}
	case 7:
		{
			edit_gui_io_addLink(item->data2);
			struct elog_xml_link *lnk = item->data2;
			lnk->num =
			    edit_gui_al_textLink(lnk->start, lnk->end,
						 lnk->href);
			break;
		}
	}

	edit_gui_io_addRedo(item);	//adding item so it can be redone
}


void edit_gui_io_addRedo(struct edit_gui_io_undoAction *item)
{
	if (item == NULL)
		return;

	if (_redo == NULL) {
		item->next = NULL;
		_redo = item;
	} else {
		item->next = _redo;
		_redo = item;
	}
}
void edit_gui_io_clearRedo()
{
	while (_redo != NULL) {
		if (_redo->action == EDIT_GUI_IO_ACTION_CUT ||
		    _redo->action == EDIT_GUI_IO_ACTION_PASTE)
			free(_redo->data2);

		struct edit_gui_io_undoAction *p = _redo;
		_redo = _redo->next;
		if (p->action == EDIT_GUI_IO_ACTION_LINK)
			elog_xml_lnk_free(p->data2);
		free(p);
	}
	//_redo is= NULL;
}
void edit_gui_io_printStat()
{
	struct edit_gui_io_undoAction *p = _redo;
	while (p != NULL) {
		switch (p->action) {
		case 0:
			{

				break;
			}
		case 1:
			{
				//printf("Char:%c @ %i\n", p->data, p->loc);
				break;
			}
		case 2:
			{
				//printf("Del:%c @ %i\n", p->data,p->loc); 
				break;
			}
		}
		p = p->next;
	}
	printf("\n/****************************************/\n");
}

void edit_gui_io_addUndo(struct edit_gui_io_undoAction *item)
{				//internal function.
	if (item == NULL)
		return;
	if (_undo == NULL) {
		_undo = item;
		item->next = NULL;
	} else {
		item->next = _undo;
		_undo = item;
	}
}
void edit_gui_io_redo()
{
	if (_redo == NULL)
		return;
	struct edit_gui_io_undoAction *item = _redo;
	_redo = _redo->next;

	switch (item->action) {
	case 0:
		{

			break;
		}
	case 1:
		{
			//handling spaces
			if (item->data == ' ' || item->data == '	'
			    || item->data == '\n') {
				edit_gui_al_insertChar(item->loc,
						       item->data);
				break;
			}

			struct edit_gui_io_undoAction *p = item->next;
			int loc = item->loc;
			int firstLoc = loc;
			while (p != NULL && p->loc >= 0 && p->action == 1
			       && (p->data != ' ' && p->data != '	'
				   && p->data != '\n')
			       && loc - p->loc == -1) {
				loc = p->loc;
				p = p->next;
			}
			int c = 0;

			while (item != p) {
				edit_gui_al_insertChar(item->loc,
						       item->data);
				_redo = item->next;
				edit_gui_io_addUndo(item);
				item = _redo;
				++c;
			}	//passing onto undo

			edit_gui_io_updateTags(firstLoc, c);
			item = NULL;
			break;
		}
	case 2:
		{
			edit_gui_io_updateTags(item->loc, 1);
			edit_gui_al_removeChar(item->loc);
			break;
		}
	case 4:
		{
			edit_gui_io_updateTags(item->loc,
					       -1 * elog_gui_al_strlen(item->
								  data2));
			edit_gui_al_removeStr(item->loc,
					      elog_gui_al_strlen(item->
								 data2));
			break;
		}
	case 5:
		{
			edit_gui_io_updateTags(item->loc,
					       elog_gui_al_strlen(item->
								  data2));
			edit_gui_al_insertStr(item->loc, item->data2);
			break;
		}
	case 6:
		{
			edit_gui_io_addLink(item->data2);
			struct elog_xml_link *lnk = item->data2;
			lnk->num =
			    edit_gui_al_textLink(lnk->start, lnk->end,
						 lnk->href);
			break;
		}
	case 7:
		{
			struct elog_xml_link *lnk = item->data2;
			edit_gui_io_removeLink(lnk->start - 1);
			break;
		}
	}

	edit_gui_io_addUndo(item);	//adding item so it can be redone         
}
